package com.xgimi.locales.utils

import com.xgimi.locales.bean.*
import com.xgimi.locales.ext.ifExistsMakesByStrings
import org.dom4j.CDATA
import org.dom4j.Document
import org.dom4j.DocumentHelper
import org.dom4j.Element
import org.dom4j.io.OutputFormat
import org.dom4j.io.SAXReader
import org.dom4j.io.XMLWriter
import java.io.File
import java.io.FileOutputStream

object XmlParserUtil {

    //将RowBean写入xml
    fun writeRowBeanByXml(
        inputConfig: InputConfig,
        progress: (msg: String) -> Unit,
        error: (msg: String) -> Unit,
        complete: () -> Unit
    ) {
        // 检查创建项目中缺失的文件夹与文件
        if (inputConfig.isAutoCompletionDirOrFile) {
            inputConfig.selectModuleBeans?.forEach { moduleBean ->
                println("writeRowBeanByXml $moduleBean")
                // 默认 module app
                inputConfig.selectLangs?.forEach { selectLang ->
                    val find = moduleBean?.xmlFiles?.find { it.langName == selectLang }
                    if (null == find) { // 未找到，补全资源目录(values-xx)
                        val newXmlFileBean = XmlFileBean()
                        newXmlFileBean.filePath =
//                            File(moduleBean?.moduleFilePath, "src/main/res/values-${selectLang}/strings.xml")
                            File(moduleBean?.moduleFilePath, "${moduleBean?.moduleResPath}/values-${selectLang}/strings.xml")
                                .ifExistsMakesByStrings(inputConfig.isAutoCompletionDirOrFile).absolutePath
                        newXmlFileBean.langName = selectLang
                        moduleBean?.xmlFiles?.add(newXmlFileBean)
//                        progress("writeRowBeanByXml add new value dir：${newXmlFileBean.filePath}")
                    }
                }
            }
        }

        //将rowBean按照module进行分类，提高写入速度
        inputConfig.rows!!.groupBy { row ->
            inputConfig.selectModuleBeans?.find {
                if (row.rowAttr?.moduleName.isNullOrEmpty()) {
                    // 兼容python脚本，只能写入选择的一个模块(比如app)
                    true
                } else {
                    row.rowAttr?.moduleName == it.moduleName
                }
            }
        }.filter { it.key != null }.forEach { (moduleBean, rows) ->
            val allLangRows = moduleBean?.xmlFiles?.toMutableList()
            // 加入默认语种，写入values目录的strings.xml
            val defLang = XmlFileBean()
            defLang.filePath = File(moduleBean?.moduleFilePath, "${moduleBean?.moduleResPath}/values/strings.xml")
                    .ifExistsMakesByStrings(inputConfig.isAutoCompletionDirOrFile).absolutePath
            defLang.langName = Constant.DEF_LANG
            allLangRows?.add(defLang)
            //将每个module的row按照语言类型写入，python脚本导入的，只能默认写入勾选的模块.
            allLangRows?.forEach { xmlFileBean ->
                // TODO：处理未选中语言跳过的问题
                //
                val xmlFile = File(xmlFileBean.filePath).ifExistsMakesByStrings(inputConfig.isAutoCompletionDirOrFile)
                var doc: Document? = null
                if (!xmlFile.exists()) { // 新增 xml 文件
                    if (inputConfig.isAutoCompletionDirOrFile) {
                        xmlFile.createNewFile()
//                        progress("writeRowBeanByXml create new xml file：${xmlFileBean.filePath}")
                        doc = createResXmlElement()
                        addRowToXmlDoc(rows!!, xmlFileBean, progress, inputConfig, doc)
                    }
                } else {
                    if (!xmlFile.exists()) {
                        error("文件未找到：${xmlFileBean.filePath}")
                        return@forEach
                    } else { // XML文件存在，新增\修改字段
                        try {
                            doc = SAXReader().read(xmlFile)
                        } catch (e: Exception) {
                            e.printStackTrace()
                        } finally {
                            // 修复资源文件存在，无内容崩溃的问题，导致无法输入翻译
                            // FIXME:这里会导致XML文件 重新创建，覆盖了之前的XML文件.
//                            if (null == doc) {
//                                doc = createResXmlElement()
//                            }
                            try {
                                if (null == doc) {
                                    doc = SAXReader().read(xmlFile)
                                }
                            } catch (e:Exception) {
                                e.printStackTrace()
                                error("$xmlFile 读取失败 ${e.message}")
                            }
                        }
                        //
                        if (null != doc) {
                            addRowToXmlDoc(rows!!, xmlFileBean, progress, inputConfig, doc)
                        }
                    }
                }
                // 写入xml文件
                if (null != doc) {
                    writeDocToFile(doc, xmlFile)
                }
//                println("write over：${xmlFileBean.filePath} ")
            }
        }
        //
        complete()
    }

    /**
     * 创建 resources xml 元素
     */
    private fun createResXmlElement():Document {
        val doc = DocumentHelper.createDocument()
        val rootElement = DocumentHelper.createElement("resources")
        doc.rootElement = rootElement
        return doc
    }


    /**
     * @rows xml 文档 的 一行的信息 存储 的 list列表 @see RowBean
     */
    private fun addRowToXmlDoc(
        rows: List<RowBean>,
        xmlFileBean: XmlFileBean,
        progress: (msg: String) -> Unit,
        inputConfig: InputConfig,
        doc: Document
    ) {
        rows.forEach { row->
            val lang = xmlFileBean.langName
            // 设置默认语种
            //
            if (row.langs.containsKey(lang)) {
                val value = row.langs[lang]
                // TODO: 先默认使用 value
//                val insertValue = if (inputConfig.isSmartFix) value?.stringChartFormat(lang) else value
                var insertValue = value?.replace(" ", " ") ?: ""
                // & 转义为 &amp;
                while (insertValue.indexOf("&") != -1 && !insertValue.startsWith("&amp;", insertValue.indexOf("&"))) {
                    insertValue = insertValue.replace("&", "&amp;")
                }
                // 处理 双引号无法显示的问题
                while (insertValue.indexOf("\"") != -1 && !insertValue.startsWith("\\\"", insertValue.indexOf("\"")-1)) {
                    insertValue = insertValue.replace("\"", "\\\"")
                }
                // 处理 单引号 错误问题
                while (insertValue.indexOf("\'") != -1 && !insertValue.startsWith("\\\'", insertValue.indexOf("\'")-1)) {
                    insertValue = insertValue.replace("\'", "\\\'")
                }
                //
                if (row.rowAttr?.isArray == true) { // 数组 string-array
                    val selectNodes = doc.selectNodes("/resources/string-array[@name='${row.rowAttr!!.keyName}']")
                    if (!selectNodes.isNullOrEmpty()) {
                        val arrayElement = selectNodes.first() as? Element
                        arrayElement?.elements()?.also { items ->
                            if (items.size > row.rowAttr!!.arrayItemIndex) {//重复字段直接覆盖内容
                                // 判断内容不一致才写入
                                val text = items[row.rowAttr!!.arrayItemIndex].text
                                if (text != insertValue) {
                                    items[row.rowAttr!!.arrayItemIndex].text = insertValue
                                }
                            } else {//新增item
                                val newItem = DocumentHelper.createElement("item")
                                newItem.text = insertValue
                                arrayElement.add(newItem)
                            }
                        }
                    } else {//新增string-array
                        val newArrayElement = DocumentHelper.createElement("string-array")
                        newArrayElement.addAttribute("name", row.rowAttr!!.keyName)
                        val newItem = DocumentHelper.createElement("item")
                        newItem.text = insertValue
                        newArrayElement.add(newItem)
                        doc.rootElement.add(newArrayElement)
                    }
                } else { // string
                    // 判断字段是否已存在
                    val selectNodes = doc.selectNodes("/resources/string[@name='${row.rowAttr?.keyName}']")
//                    println("addRowToXmlDoc string selectNodes:${selectNodes}")
                    if (!selectNodes.isNullOrEmpty()) {
                        // 内容不一致才写入
                        if (selectNodes.first().text != insertValue) {
                            selectNodes.first().text = insertValue
                        }
                    } else {
                        val element = DocumentHelper.createElement("string")
                        element.addAttribute("name", row.rowAttr?.keyName)
                        element.text = insertValue
                        doc.rootElement.add(element)
                    }
                }
                //
                progress("写入字段:${row.rowAttr?.keyName}->$value")
            }
        }
    }

    private fun writeDocToFile(document: Document, file: File) {
        val fileOutputStream = FileOutputStream(file)
        val format = OutputFormat()
        format.encoding = "UTF-8"
        format.isNewlines = true
        format.indent = "\t"
        format.isTrimText = true//去掉之前的空格
        val xmlWriter = XMLWriter(fileOutputStream, format)
        //设置是否转义。默认true，代表转义
//        xmlWriter.isEscapeText = false
        xmlWriter.isEscapeText = true
        xmlWriter.write(document)
        xmlWriter.flush()
        xmlWriter.close()
    }

    /**
     * 将xml转化未RowBean
     * 将选择的模块循环遍历，将模块内的string.xml循环遍历处理，
     * 最后将 hashmap 的 模块信息 对应 RowBean列表返回.
     */
    fun parseXmlByModule(config: ExportConfig, block: (RowBean) -> Unit): HashMap<ModuleBean, ArrayList<RowBean>> {
        println("parseXmlByModule start")
        val hashMap = HashMap<ModuleBean, ArrayList<RowBean>>()
        config.selectModuleBeans?.forEach { module ->
            val rows = ArrayList<RowBean>()
            module.xmlFiles.forEach { file ->
                if (File(file.filePath).exists()) {
                    val elementIterator = SAXReader().read(file.filePath).rootElement.elementIterator()
                    while (elementIterator.hasNext()) {
                        val element = elementIterator.next()
                        if (Constant.STRING_ARRAY_XML_NAME == element.name) { // string-array
                            val key = element.attribute("name").value
                            element.elements().forEachIndexed { index, arrayItem ->
                                val isCdata = arrayItem.content().any { it is CDATA }
                                var cellValue = arrayItem.text
                                if (isCdata) cellValue = "<![CDATA[$cellValue]]>"
                                val row = rows.find { it.rowAttr?.keyName == key && it.rowAttr?.arrayItemIndex == index }
                                    ?: RowBean().apply {
                                        if (rowAttr == null) {
                                            rowAttr = RowAttr()
                                        }
                                        rowAttr?.isArray = true
                                        rowAttr?.keyName = key
                                        rowAttr?.arrayItemIndex = index
                                        rowAttr?.moduleName = module.moduleName
                                        syncRowAttr()
                                        rows.add(this)
                                    }
                                row.langs[file.langName] = cellValue
                                block(row)
                            }
                        } else { // string
                            val isCdata = element.content().any { it is CDATA }
                            var cellValue = element.text
                            val key = element.attribute("name").value
                            if (isCdata) cellValue = "<![CDATA[$cellValue]]>"
                            val row = rows.find { it.rowAttr?.keyName == key && !it.rowAttr?.isArray!! } ?: RowBean().apply {
                                if (rowAttr == null) {
                                    rowAttr = RowAttr()
                                }
                                rowAttr?.keyName = key
                                rowAttr?.moduleName = module.moduleName
                                syncRowAttr()
                                rows.add(this)
                            }
                            row.langs[file.langName] = cellValue
                            block(row)
                        }
                    }
                }
            }
            println("parseXmlByModule module:$module rows:$rows")
            hashMap[module] = rows
        }
        println("parseXmlByModule end")
        return hashMap
    }

}
